C++ Basics

Static methods / non-static methods

Let's start with a basic example, illustrating how can static functions can affect the way you're going to organize you're code.

Usage scenario:

Let's assume that you're building an application that handles access to an MySql database that accepts only one connection at a time, and normaly when an user uses you app you will want to provide access for delete, create, update records in database.

Developed as an Client - Server architecture you can have at the same time multiple users that are requesting access to the database.

Solution:

As covered in chapter 1.2 you will have to have some complex algorithm that will allow you to share the same connection (a static instance of an connection object) between all users and handle the requests in your program sequentially.

Usually the implementation of this "complex" algorithm consist in a static function that takes care that the connection object is only instantiated once during runtime and if instantiated it's instance is shared to all requesting clients.

Static methods

We could easily say that a static method is a method that has static qualifier in front of it's declaration.

Those methods cannot be called on instaces of the class that they're declared in. They can only be called using scope operators.

Example:

class Str
{
        std::string string;
    public:
        int length();
        static Str stringFromNumber(int number);
};

//other context
int number = 25;
Str string = Str::stringFromNumber(number);
cout<<"length of converted string is "<<string.length();

Method stringFromNumber cannot be called on an instance of Str and also to call it we need to use scope resolution identifier Str::

Regarding static methods there are some usage scenarios and design patterns that we're going to cover later in other chapter of the book.

A short guideline about when you should use/define static methods:

  • If you are writing utility classes and they are not supposed to be changed.
  • If the method is not using any instance variable.
  • If any operation is not dependent on instance creation.
  • If there is some code that can easily be shared by all the instance methods, extract that code into a static method.
  • If you are sure that the definition of the method will never be changed or overridden. As static methods can not be overridden

Non-static methods

A non-static member function is a function that is declared in a member specification of a class without a static or friend specifier.

class S 
{
    int mf1(); // non-static member function declaration
    void mf2() volatile, mf3() &&; // can be cv-qualified and reference-qualify
    int mf4() const { return data; } // can be defined inline
    virtual void mf5() final; // can be virtual, can use final/override
    S() : data(12) {} // constructors are member functions too
    int data;
};

// if not defined inline, has to be defined at namespace
int S::mf1() 
{ 
    return 7; 
}

Any function declarations are allowed, with additional syntax elements that are only available for non-static member functions: final and override specifiers, pure-specifiers, cv-qualifiers, ref-qualifiers, and member initialization lists.

A non-static member function of class X may be called

  1. For an object of type X using the class member access operator
  2. For an object of a class derived from X
  3. Directly from within the body of a member function of X
  4. Directly from within the body of a member function of a class derived from X

Calling a member function of class X on an object of any other type invokes undefined behavior.

Within the body of a non-static member function of X, any id-expression E (e.g. an identifier) that resolves to a non-type non-static member of X or of a base class of X, is transformed to a member access expression (*this).E (unless it's already a part of a member access expression).

struct S {
    int n;
    void f();
};
void S::f() {
    n = 1; // transformed to (*this).n = 1;
}
int main() {
    S s1, s2;
    s1.f(); // changes s1.n
}

Within the body of a non-static member function of X, any unqualified-id that resolves to a static member, an enumerator or a nested type of X or of a base class of X, is transformed to the corresponding qualified-id.

struct S {
    static int n;
    void f();
};
void S::f() {
    n = 1; // transformed to S::n = 1;
}
int main() {
    S s1, s2;
    s1.f(); // changes S::n
}